
#include <errno.h>
#include <sys/mman.h>

#include <stdlib.h>
#include <stdio.h>
#include <libv4l2.h>

#include "../../util/mem.h"
#include "../../util/list.h"

#include "v4l.h"
#include "v4l_common.h"
#include "v4l2_driver.h"

#define TRUE (1==1)
#define FALSE (1==0)

/* = Getters ================================================================ */
int v4l2_driver_query_capability(int fd, struct v4l2_capability * vc) {
	return xioctl(fd, VIDIOC_QUERYCAP, vc) >= 0;
}

int v4l2_driver_query_control(int fd, struct v4l2_queryctrl * qc) {
	return xioctl(fd, VIDIOC_QUERYCTRL, qc) >= 0;
}

int v4l2_driver_query_menu(int fd, struct v4l2_querymenu * qm) {
	return xioctl(fd, VIDIOC_QUERYMENU, qm) >= 0;
}

int v4l2_driver_query_cropcap(int fd, struct v4l2_cropcap * cropcap) {
	return xioctl(fd, VIDIOC_CROPCAP, cropcap) >= 0;
}

int v4l2_driver_get_control_class(int fd, struct v4l2_queryctrl * qctrl, int cls) {
	/**
	 * 
	 * @param fd
	 * @param qctrl
	 * @param class V4L2_CTRL_TYPE_CTRL_CLASS and others
	 * @return 
	 */
	memset(qctrl, 0, sizeof (qctrl));
	qctrl->id = cls + 1;
	return v4l2_driver_query_control(fd, qctrl);
}

int v4l2_driver_get_control(int fd, struct v4l2_control * ctrl, int ctrl_id) {
	memset(ctrl, 0, sizeof (struct v4l2_control));
	ctrl->id = ctrl_id;
	return xioctl(fd, VIDIOC_G_CTRL, ctrl) >= 0;
}

int v4l2_driver_get_standard_id(int fd, v4l2_std_id * std_id) {
	return xioctl(fd, VIDIOC_G_STD, std_id) >= 0;
}

int v4l2_driver_get_standard(int fd, struct v4l2_standard * standard) {

	v4l2_std_id std_id;

	if (xioctl(fd, VIDIOC_G_STD, &std_id) < 0) return 0;

	memset(standard, 0, sizeof (struct v4l2_standard));

	standard->index = 0;
	while (-1 != xioctl(fd, VIDIOC_ENUMSTD, standard)) {

		if (standard->id == std_id) {
			return 1;
		}
		standard->index++;
	}
	return 0;
}

int v4l2_driver_get_format(int fd, struct v4l2_format * fmt, enum v4l2_buf_type type) {
	memset(fmt, 0, sizeof (struct v4l2_format));
	fmt->type = type;
	return xioctl(fd, VIDIOC_G_FMT, fmt) >= 0;
}

int v4l2_driver_get_format_description(int fd, struct v4l2_fmtdesc * fmtdesc, enum v4l2_buf_type type) {

	struct v4l2_format format;
	if (!v4l2_driver_get_format(fd, &format, type)) return 0;

	if (v4l2_driver_enum_format_description(fd, fmtdesc, TRUE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
		do {

			if (fmtdesc->pixelformat == format.fmt.pix.pixelformat) break;

		} while (v4l2_driver_enum_format_description(fd, fmtdesc, FALSE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE));
	}

	return 1;
}

int v4l2_driver_get_input(int fd, struct v4l2_input * input) {
	int r;
	int index;

	r = xioctl(fd, VIDIOC_G_INPUT, &index);
	if (-1 == r) {
		return 0;
	}
	input->index = index;
	return xioctl(fd, VIDIOC_ENUMINPUT, input) >= 0;
}

int v4l2_driver_get_interval(int fd, struct v4l2_fract * interval) {
	struct v4l2_streamparm parm;

	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (xioctl(fd, VIDIOC_G_PARM, &parm) >= 0 && (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
		*interval = parm.parm.capture.timeperframe;
		return 1;
	}
	return 0;
}

/* = Setters ================================================================ */
int v4l2_driver_set_standard(int fd, v4l2_std_id * std_id) {
	return xioctl(fd, VIDIOC_S_STD, std_id) >= 0;
}

int v4l2_driver_set_standard_id(int fd, v4l2_std_id * std_id) {
	return xioctl(fd, VIDIOC_S_STD, std_id) >= 0;
}

int v4l2_driver_set_format(int fd, struct v4l2_format * fmt) {
	return xioctl(fd, VIDIOC_S_FMT, fmt) >= 0;
}

int v4l2_driver_set_fps(int fd, int fps) {
	struct v4l2_format fmt;

	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	int r = xioctl(fd, VIDIOC_G_FMT, &fmt);

#define PWC_FPS_SHIFT		16
#define PWC_FPS_MASK		0x00FF0000
#define PWC_FPS_FRMASK		0x003F0000

	fmt.fmt.pix.priv &= ~PWC_FPS_FRMASK;
	fmt.fmt.pix.priv |= (fps << PWC_FPS_SHIFT);


	if (xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
		printf("set frame rate failed\n");
		return -1;
	}

}

int v4l2_driver_set_crop(int fd, struct v4l2_crop * crop) {
	return xioctl(fd, VIDIOC_S_CROP, crop) >= 0;
}

int v4l2_driver_set_input(int fd, int * input) {
	return xioctl(fd, VIDIOC_S_INPUT, input) >= 0;
}

int v4l2_driver_set_interval(int fd, struct v4l2_fract * interval) {
	struct v4l2_streamparm parm;

	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (xioctl(fd, VIDIOC_G_PARM, &parm) < 0) return 0;

	if (!(parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) return 0;

	parm.parm.capture.timeperframe = *interval;

	return xioctl(fd, VIDIOC_S_PARM, &parm) >= 0;
}

int v4l2_driver_set_control(int fd, struct v4l2_control * ctrl) {
	return xioctl(fd, VIDIOC_S_CTRL, ctrl) >= 0;
}

/* = Enumerations =========================================================== */
int v4l2_driver_enum_input(int fd, struct v4l2_input * input, int init, int index) {

	if (init) {
		memset(input, 0, sizeof (*input));
		input->index = index;
	} else {
		input->index++;
	}
	return xioctl(fd, VIDIOC_ENUMINPUT, input) >= 0;
}

int v4l2_driver_enum_output(int fd, struct v4l2_output * output, int init, int index) {

	if (init) {
		memset(output, 0, sizeof (*output));
		output->index = index;
	} else {
		output->index++;
	}
	return xioctl(fd, VIDIOC_ENUMOUTPUT, output) >= 0;
}

int v4l2_driver_enum_audio(int fd, struct v4l2_audio * audio, int init, int index) {

	if (init) {
		memset(audio, 0, sizeof (*audio));
		audio->index = index;
	} else {
		audio->index++;
	}
	return xioctl(fd, VIDIOC_ENUMAUDIO, audio) >= 0;
}

int v4l2_driver_enum_audioout(int fd, struct v4l2_audioout * audioout, int init, int index) {

	if (init) {
		memset(audioout, 0, sizeof (*audioout));
		audioout->index = index;
	} else {
		audioout->index++;
	}
	return xioctl(fd, VIDIOC_ENUMAUDOUT, audioout) >= 0;
}

int v4l2_driver_enum_standard(int fd, struct v4l2_standard * standard, int init, int index) {

	if (init) {
		memset(standard, 0, sizeof (*standard));
		standard->index = index;
	} else {
		standard->index++;
	}
	return xioctl(fd, VIDIOC_ENUMSTD, standard) >= 0;
}

int v4l2_driver_enum_format_description(int fd, struct v4l2_fmtdesc * fmtdesc, int init, int index, enum v4l2_buf_type type) {

	if (init) {
		memset(fmtdesc, 0, sizeof (*fmtdesc));
		fmtdesc->index = index;
	} else {
		fmtdesc->index++;
	}
	fmtdesc->type = type;
	return xioctl(fd, VIDIOC_ENUM_FMT, fmtdesc) >= 0;
}

int v4l2_driver_enum_frame_size(int fd, struct v4l2_frmsizeenum * frmsize, int init, int pixel_format, int index) {

	if (init) {
		memset(frmsize, 0, sizeof (*frmsize));
		frmsize->index = index;
	} else {
		frmsize->index++;
	}
	frmsize->pixel_format = pixel_format;
	return xioctl(fd, VIDIOC_ENUM_FRAMESIZES, frmsize) >= 0;
}

int v4l2_driver_enum_frame_interval(int fd, struct v4l2_frmivalenum * frminterval, int init, int pixel_format, int w, int h, int index) {

	if (init) {
		memset(frminterval, 0, sizeof (*frminterval));
		frminterval->index = index;
		frminterval->width = w;
		frminterval->height = h;
	} else {
		frminterval->index++;
	}
	frminterval->pixel_format = pixel_format;
	return xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, frminterval) >= 0;
}

int v4l2_driver_enum_control(int fd, struct v4l2_queryctrl * qctrl, int init) {

	if (init) {
		memset(qctrl, 0, sizeof (qctrl));
		qctrl->id = V4L2_CTRL_FLAG_NEXT_CTRL;
	} else qctrl->id |= V4L2_CTRL_FLAG_NEXT_CTRL;

	return v4l2_driver_query_control(fd, qctrl);
}

int v4l2_driver_enum_control_user(int fd, struct v4l2_queryctrl * qctrl, int init) {

	static int __id;

	qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;

	if (init) {
		memset(qctrl, 0, sizeof (qctrl));
		__id = V4L2_CID_BASE;
	} else {

		__id++;
		if (qctrl->id >= V4L2_CID_LASTP1) return 0;
	}

	qctrl->id = __id;

	int r = v4l2_driver_query_control(fd, qctrl);


	if (qctrl->type < 1 || qctrl->type > 8) {
		// ignore result
		__id++;
		return v4l2_driver_enum_control_user(fd, qctrl, init);
	}

	return r;

}

int v4l2_driver_enum_control_private(int fd, struct v4l2_queryctrl * qctrl, int init) {

	qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;

	if (init) {
		memset(qctrl, 0, sizeof (qctrl));
		qctrl->id = V4L2_CID_PRIVATE_BASE;
	} else {
		qctrl->id++;
	}

	return v4l2_driver_query_control(fd, qctrl);
}

int v4l2_driver_enum_control_menu(int fd, struct v4l2_querymenu * qmenu, int ctrl_id, int index) {

	memset(qmenu, 0, sizeof (*qmenu));
	qmenu->id = ctrl_id;
	qmenu->index = index;

	return v4l2_driver_query_menu(fd, qmenu);
}

/* = Others ================================================================= */
int v4l2_driver_request_buffers(int fd, struct v4l2_requestbuffers * req_buff) {
	return xioctl(fd, VIDIOC_REQBUFS, req_buff) >= 0;
}

int v4l2_driver_query_buffer(int fd, struct v4l2_buffer * query_buff) {
	return xioctl(fd, VIDIOC_QUERYBUF, query_buff) >= 0;
}

int v4l2_driver_enqueue_buffer(int fd, struct v4l2_buffer * queue_buff) {
	return xioctl(fd, VIDIOC_QBUF, queue_buff) >= 0;
}

int v4l2_driver_dequeue_buffer(int fd, struct v4l2_buffer * queue_buff) {
	return xioctl(fd, VIDIOC_DQBUF, queue_buff) >= 0;
}

int v4l2_driver_streamon(int fd, enum v4l2_buf_type type) {
	return xioctl(fd, VIDIOC_STREAMON, &type) >= 0;
}

int v4l2_driver_streamoff(int fd, enum v4l2_buf_type type) {
	return xioctl(fd, VIDIOC_STREAMOFF, &type) >= 0;
}

/* = Get lists ============================================================== */
List * v4l2_driver_get_frame_size_list(int fd) {

	struct v4l2_frmsizeenum frmsize;
	struct v4l2_frmsizeenum * frmsize_new;
	struct v4l2_format fmt;

	List * l;

	//CLEAR(fmt);
	memset(&fmt, 0, sizeof (fmt));
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	// get pixel format
	if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) {
		return NULL;
	}

	l = list_new();

	frmsize.pixel_format = fmt.fmt.pix.pixelformat;
	frmsize.index = 0;
	frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE;

	while (-1 != xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) {

		frmsize_new = (struct v4l2_frmsizeenum *) calloc(1, sizeof (struct v4l2_frmsizeenum));
		*frmsize_new = frmsize;

		list_insert(l, frmsize_new);

		frmsize.index++;
	}

	return l;
}

List * v4l2_driver_get_frame_intervals_list(int fd) {

	/*
		struct v4l2_frmivalenum frmival;
		struct v4l2_fract curr;
		int curr_ok, ok;
	 */


	//v4l2_driver_get_

	/*
		m_frameInterval->clear();

		ok = enum_frameintervals(frmival, m_pixelformat, m_width, m_height);
		curr_ok = get_interval(curr);
		if (ok && frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
			do {
				m_frameInterval->addItem(QString("%1 fps")
						.arg((real) frmival.discrete.denominator / frmival.discrete.numerator));
				if (curr_ok &&
						frmival.discrete.numerator == curr.numerator &&
						frmival.discrete.denominator == curr.denominator)
					m_frameInterval->setCurrentIndex(frmival.index);
			} while (enum_frameintervals(frmival));
		}
	 */
	return NULL;

}

List * v4l2_driver_get_frame_size_list_test(int fd) {

	struct v4l2_frmivalenum frmival;
	struct v4l2_frmivalenum * frmival_new;
	struct v4l2_format fmt;

	List * l;

	//CLEAR(fmt);
	memset(&fmt, 0, sizeof (fmt));
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	// get pixel format
	if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) {
		return NULL;
	}

	l = list_new();

	frmival.pixel_format = fmt.fmt.pix.pixelformat;
	frmival.index = 0;
	frmival.type = V4L2_FRMIVAL_TYPE_DISCRETE;
	frmival.width = fmt.fmt.pix.width;
	frmival.height = fmt.fmt.pix.height;


	while (-1 != xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival)) {

		frmival_new = (struct v4l2_frmivalenum *) calloc(1, sizeof (struct v4l2_frmivalenum));
		*frmival_new = frmival;

		list_insert(l, frmival_new);

		frmival.index++;
	}

	return l;
}

List * v4l2_driver_get_pixel_format_list(int fd) {

	List * l;
	struct v4l2_fmtdesc fmtdesc;
	struct v4l2_fmtdesc * fmtdesc_new;

	l = list_new();

	/*
		memset(&fmtdesc, 0, sizeof (struct v4l2_fmtdesc));
		fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		fmtdesc.index = 0;
	 */

	if (v4l2_driver_enum_format_description(fd, &fmtdesc, TRUE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE)) {
		do {
			fmtdesc_new = (struct v4l2_fmtdesc *) calloc(1, sizeof (struct v4l2_fmtdesc));
			*fmtdesc_new = fmtdesc;
			list_insert(l, fmtdesc_new);
		} while (v4l2_driver_enum_format_description(fd, &fmtdesc, FALSE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE));
	}


	/*
		while (-1 != xioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) {

			fmtdesc_new = (struct v4l2_fmtdesc *) calloc(1, sizeof (struct v4l2_fmtdesc));
	 *fmtdesc_new = fmtdesc;

			list_insert(l, fmtdesc_new);

			fmtdesc.index++;
		}
	 */

	return l;
}

List * v4l2_driver_get_input_list(int fd) {


	struct v4l2_input input;
	struct v4l2_input * input_new;

	//CLEAR(input);
	memset(&input, 0, sizeof (input));

	List * l;
	l = list_new();

	input.index = 0;
	while (-1 != xioctl(fd, VIDIOC_ENUMINPUT, &input)) {

		input_new = (struct v4l2_input *) calloc(1, sizeof (struct v4l2_input));
		*input_new = input;

		list_insert(l, input_new);

		input.index++;
	}

	return l;
}

List * v4l2_driver_get_standard_list(int fd) {

	struct v4l2_standard std;
	struct v4l2_standard * std_new;

	List * l;
	l = list_new();

	memset(&std, 0, sizeof (std));

	std.index = 0;
	while (-1 != xioctl(fd, VIDIOC_ENUMSTD, &std)) {

		std_new = (struct v4l2_standard *) calloc(1, sizeof (struct v4l2_standard));
		*std_new = std;
		list_insert(l, std_new);

		std.index++;
	}

	return l;
}

static int __is_valid_ctrl_type(enum v4l2_ctrl_type type) {
	switch (type) {
		case V4L2_CTRL_TYPE_INTEGER:
		case V4L2_CTRL_TYPE_BOOLEAN:
		case V4L2_CTRL_TYPE_MENU:
		case V4L2_CTRL_TYPE_BUTTON:
		case V4L2_CTRL_TYPE_INTEGER64:
			//case V4L2_CTRL_TYPE_BITMASK:
		case V4L2_CTRL_TYPE_CTRL_CLASS:
		case V4L2_CTRL_TYPE_STRING:
			return 1;
		default:
			return 0;
	}
}

List * v4l2_driver_get_ctrl_list(int fd) {

	List * list = NULL;
	struct v4l2_queryctrl qctrl;

	memset(&qctrl, 0, sizeof (qctrl));
	qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;

	list = list_new();

	while (v4l2_driver_query_control(fd, &qctrl)) {

		if (__is_valid_ctrl_type(qctrl.type) && (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) == 0) {

			struct v4l2_queryctrl * qctrl_new;

			qctrl_new = mem_alloc(struct v4l2_queryctrl);

			*qctrl_new = qctrl;
			list_append(list, qctrl_new);
		}
		qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
	}

	if (qctrl.id == V4L2_CTRL_FLAG_NEXT_CTRL) {
		qctrl.id = V4L2_CTRL_CLASS_USER | 1;
		qctrl.type = V4L2_CTRL_TYPE_CTRL_CLASS;
		int id;
		for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
			qctrl.id = id;
			if (!v4l2_driver_query_control(fd, &qctrl)) {
				continue;
			}
			if (!__is_valid_ctrl_type(qctrl.type)) {
				continue;
			}
			if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
				continue;
			}

			struct v4l2_queryctrl * qctrl_new;
			qctrl_new = mem_alloc(struct v4l2_queryctrl);
			*qctrl_new = qctrl;
			list_append(list, qctrl_new);

		}

		for (qctrl.id = V4L2_CID_PRIVATE_BASE; v4l2_driver_query_control(fd, &qctrl); qctrl.id++) {

			if (!__is_valid_ctrl_type(qctrl.type)) {
				continue;
			}
			if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
				continue;
			}


			struct v4l2_queryctrl * qctrl_new;
			qctrl_new = mem_alloc(struct v4l2_queryctrl);
			*qctrl_new = qctrl;
			list_append(list, qctrl_new);
		}
	}


	if (list->size == 0) {
		list_delete(list);
		list = NULL;
	}

	return list;
}

/*
List * v4l2_driver_get_user_ctrl_list(int fd) {

	List * list = NULL;
	struct v4l2_queryctrl qctrl;

	memset(&qctrl, 0, sizeof (qctrl));
	qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;

	list = list_new();

	qctrl.id = V4L2_CTRL_CLASS_USER | 1;
	qctrl.type = V4L2_CTRL_TYPE_CTRL_CLASS;
	int id;
	for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
		qctrl.id = id;
		if (!v4l2_driver_queryctrl(fd, &qctrl)) {
			continue;
		}
		if (!__is_valid_ctrl_type(qctrl.type)) {
			continue;
		}
		if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
			continue;
		}

		struct v4l2_queryctrl * qctrl_new;
		qctrl_new = mem_alloc(struct v4l2_queryctrl);
 *qctrl_new = qctrl;
		list_append(list, qctrl_new);

	}

	for (qctrl.id = V4L2_CID_PRIVATE_BASE; v4l2_driver_queryctrl(fd, &qctrl); qctrl.id++) {

		if (!__is_valid_ctrl_type(qctrl.type)) {
			continue;
		}
		if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
			continue;
		}


		struct v4l2_queryctrl * qctrl_new;
		qctrl_new = mem_alloc(struct v4l2_queryctrl);
 *qctrl_new = qctrl;
		list_append(list, qctrl_new);
	}

	if (list->size == 0) {
		list_delete(list);
		list = NULL;
	}

	return list;
}
 */

/* =  ============================================================== */

/* = Capabilities =========================================================== */
int v4l2_driver_cap_request_buffers_mmap(int fd, struct v4l2_requestbuffers *req_buff, int nbuffers) {

	memset(req_buff, 0, sizeof (struct v4l2_requestbuffers));

	req_buff->count = nbuffers;
	req_buff->memory = V4L2_MEMORY_MMAP;
	req_buff->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	return v4l2_driver_request_buffers(fd, req_buff);
}

int v4l2_driver_cap_request_buffers_user(int fd, struct v4l2_requestbuffers * reqbuf, int count, enum v4l2_buf_type type) {

	memset(reqbuf, 0, sizeof (*reqbuf));

	reqbuf->count = count;
	reqbuf->type = type;
	reqbuf->memory = V4L2_MEMORY_USERPTR;

	return v4l2_driver_request_buffers(fd, reqbuf);
}

int v4l2_driver_cap_dequeue_buffer_mmap(int fd, struct v4l2_buffer * buf, int * again) {

	int r;

	memset(buf, 0, sizeof (*buf));
	buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf->memory = V4L2_MEMORY_MMAP;
	r = v4l2_driver_dequeue_buffer(fd, buf);
	*again = r < 0 && errno == EAGAIN;
	return r >= 0 || *again;
}

int v4l2_driver_cap_dequeue_buffer_user(int fd, struct v4l2_buffer * buf, int * again) {

	int r;

	memset(buf, 0, sizeof (*buf));
	buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf->memory = V4L2_MEMORY_USERPTR;
	r = v4l2_driver_dequeue_buffer(fd, buf);
	*again = r < 0 && errno == EAGAIN;
	return r >= 0 || *again;
}

int v4l2_driver_cap_enqueue_buffer_mmap(int fd, int index) {
	struct v4l2_buffer buf;

	memset(&buf, 0, sizeof (buf));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	buf.index = index;
	return v4l2_driver_enqueue_buffer(fd, &buf);
}

int v4l2_driver_cap_enqueue_buffer_user(int fd, int index, void * ptr, int length) {
	struct v4l2_buffer buf;

	memset(&buf, 0, sizeof (buf));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_USERPTR;
	buf.m.userptr = (unsigned long) ptr;
	buf.length = length;
	buf.index = index;
	return v4l2_driver_enqueue_buffer(fd, &buf);
}

/*===============================================*/

int v4l2_driver_mmap(int fd, struct v4l_buffers * v4lbuffers, struct v4l2_requestbuffers *reqbuf) {


	v4lbuffers->qtd = reqbuf->count;
	v4lbuffers->buffers = mem_allocn(reqbuf->count, struct buffer);

	if (!v4lbuffers->buffers) {
		return 0;
	}

	// initializing buffers
	int n_buff;
	for (n_buff = 0; n_buff < reqbuf->count; n_buff++) {
		struct v4l2_buffer buf;

		//CLEAR(buf);
		memset(&buf, 0, sizeof (buf));

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buff;
		//buf.timecode.type = V4L2_TC_TYPE_30FPS;

		if (-1 == v4l2_driver_query_buffer(fd, &buf)) {
			return 0;
		}

		v4lbuffers->buffers[n_buff].length = buf.length;
		v4lbuffers->buffers[n_buff].start =
				v4l2_mmap(NULL /* start anywhere */,
				buf.length,
				PROT_READ | PROT_WRITE /* required */,
				MAP_SHARED /* recommended */,
				fd, buf.m.offset);

		if (MAP_FAILED == v4lbuffers->buffers[n_buff].start) {
			//_msg(MSG_ERROR, "MAP_FAILED  -- could not do mmap!\n");
			return 0;
		}
	}
	return 1;

}

int v4l2_driver_mmap_old(int fd, int nbuffers, struct v4l_buffers * v4lbuffers) {

	struct v4l2_capability cap_v4l2;
	if (-1 == v4l2_driver_query_capability(fd, &cap_v4l2)) {
		//msg(MSG_ERROR, "%s is no v4l2 device!\n", vd->dev);
		return 0;
	}

	struct v4l2_cropcap cropcap;
	struct v4l2_crop crop;
	//CLEAR(cropcap);
	memset(&cropcap, 0, sizeof (cropcap));
	cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (!v4l2_driver_query_cropcap(fd, &cropcap)) {
		crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		crop.c = cropcap.defrect; /* reset to default */

		if (-1 == v4l2_driver_set_crop(fd, &crop)) {
			switch (errno) {
				case EINVAL:
					/* Cropping not supported. */
					break;
				default:
					/* Errors ignored. */
					break;
			}
		}
	} else {
		/* Errors are ignored. */
	}

	/* = init memory with MMAP ============================================== */
	if (!(cap_v4l2.capabilities & V4L2_CAP_STREAMING)) {
		//msg_warning("%s does not support streaming I/O ioctls!\n", vd->dev);
		return 0;
	}

	struct v4l2_requestbuffers req;

	//CLEAR(req);
	memset(&req, 0, sizeof (req));

	req.count = nbuffers;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;

	if (!v4l2_driver_request_buffers(fd, &req)) {
		return 0;
	}

	if (req.count < 2) {
		//_msg(MSG_ERROR, "Insufficient buffer memory on %s\n", vd->dev);
		return 0;
	}

	v4lbuffers->qtd = req.count;
	v4lbuffers->buffers = mem_allocn(req.count, struct buffer);

	if (!v4lbuffers->buffers) {
		//msg_error("Out of memory\n");
		return 0;
	}

	// initializing buffers
	int n_buff;
	for (n_buff = 0; n_buff < req.count; n_buff++) {
		struct v4l2_buffer buf;

		//CLEAR(buf);
		memset(&buf, 0, sizeof (buf));

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buff;
		buf.timecode.type = V4L2_TC_TYPE_30FPS;

		if (-1 == v4l2_driver_query_buffer(fd, &buf)) {
			return 0;
		}

		v4lbuffers->buffers[n_buff].length = buf.length;
		v4lbuffers->buffers[n_buff].start =
				mmap(NULL /* start anywhere */,
				buf.length,
				PROT_READ | PROT_WRITE /* required */,
				MAP_SHARED /* recommended */,
				fd, buf.m.offset);

		if (MAP_FAILED == v4lbuffers->buffers[n_buff].start) {
			//_msg(MSG_ERROR, "MAP_FAILED  -- could not do mmap!\n");
			return 0;
		}
	}
	return 1;
}

int v4l2_driver_enqueue_buff_x(int fd, enum v4l2_buf_type buf_type, int x) {

	struct v4l2_buffer vbuf;

	memset(&vbuf, 0, sizeof (vbuf));
	vbuf.type = buf_type;
	vbuf.memory = V4L2_MEMORY_MMAP;
	vbuf.index = x;

	return v4l2_driver_enqueue_buffer(fd, &vbuf);
}

int v4l2_driver_start(int fd, enum v4l2_buf_type buf_type) {
	return v4l2_driver_streamon(fd, buf_type);
}

int v4l2_driver_stop(int fd, enum v4l2_buf_type type) {
	int r = v4l2_driver_streamoff(fd, type);
	return r;
}

int v4l2_driver_munmap(struct v4l_buffers * v4lbuffers) {
	int i;
	for (i = 0; i < v4lbuffers->qtd; i++) {
		if (-1 == v4l2_munmap(v4lbuffers->buffers[i].start, v4lbuffers->buffers[i].length)) {
			return 0;
		}
	}
	return 1;
}
